package com.kingyea.mobilepolice.config.auth;

import com.kingyea.mobilepolice.security.DomainUserDetailsService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.intercept.RunAsManagerImpl;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

import java.util.Scanner;

/**
 * 安全权限配置
 * 为认证服务增加安全权限控制功能。
 * 当客户端程序需要获取Access Token时，会执行下面一个简单的from-login驱动的认证过程：
 */
@Order(4)// 这句很重要，没有的话，拦截放行将失效
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Value("${kingyea.mobilepolice.security.isEnableSecurity}")
    private Boolean isEnableSecurity;

    public static void main(String[] args) {
        String password = "$2a$10$XOVs4Z1YtPKqKwQVywG9j.xLAqXYRQLGZMGMrZDNbtl6pUC0Weteq";// admin
        String password2 = "$2a$10$WjApX3bMw1KfzckGCCOB.eXRNY61ZcwsqpNzc2yiHtjsqnS3LmXAS";// wyf

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        System.out.println(encoder.matches("admin", password));
        System.out.println(encoder.matches("wyf", password2));


        System.out.println(PasswordEncoderFactories.createDelegatingPasswordEncoder().matches("admin", password));
        System.out.println(PasswordEncoderFactories.createDelegatingPasswordEncoder().matches("wyf", password2));
    }

    /**
     * @Description: 自定义用户表 _user
     * @Param: []
     * @return: org.springframework.security.core.userdetails.UserDetailsService
     * @Author: cxc
     * @Date: 2018.04.03 16:41
     */
    @Bean
    @Primary
    public UserDetailsService userDetailsService() {
        return new DomainUserDetailsService();//里面的@Autowired会自动注入
    }

    /**
     * @Description: MD5+随机盐
     * @Param: []
     * @return: org.springframework.security.crypto.password.PasswordEncoder
     * @Author: cxc
     * @Date: 2018.04.03 16:40
     */
//    @Bean
//    public PasswordEncoder passwordEncoder() {
//        return new BCryptPasswordEncoder();
//    }
    // 新
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    //不定义就没有password grant_type（认证类型）

    /**
     * password 认证
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }

    @Bean
    public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
        return new SecurityEvaluationContextExtension();
    }

    /**
     * @Description: 认证管理器必须
     * @Param: []
     * @return: org.springframework.security.authentication.AuthenticationManager
     * @Author: cxc
     * @Date: 2018.04.03 17:00
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

//    protected LoginProcessingFilter buildLoginProcessingFilter() throws Exception {
//        LoginProcessingFilter filter = new LoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler);
//        filter.setAuthenticationManager(this.authenticationManager);
//        return filter;
//    }

//    /**
//     * 需要拦截的
//     * */
//    protected TokenAuthenticationProcessingFilter buildTokenAuthenticationProcessingFilter() throws Exception {
//        List<String> list = Lists.newArrayList(TOKEN_BASED_AUTH_ENTRY_POINT,MANAGE_TOKEN_BASED_AUTH_ENTRY_POINT,"/**");
//        SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(list);
//        TokenAuthenticationProcessingFilter filter = new TokenAuthenticationProcessingFilter(failureHandler, tokenExtractor, matcher);
//        filter.setAuthenticationManager(this.authenticationManager);
//        return filter;
//    }

    /**
     * @param http
     * @return void
     * @author Mr.Lin
     * @date 2018/3/30 14:19
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        if (isEnableSecurity) {
//        http.csrf().disable()
            http
                    .csrf().disable()
                    .authorizeRequests().antMatchers("/v2/api-docs",//swagger api json
                    "/swagger-resources/configuration/ui",//用来获取支持的动作
                    "/swagger-resources",//用来获取api-docs的URI
                    "/swagger-resources/configuration/security",//安全选项
                    "/swagger-ui.html"
                    , "/syssync/**","/druid/**","/druid/index.html").permitAll().and()
                    // 开启表单认证
                    .formLogin()
                    // 自定义设置未认证后要跳转的url 覆盖原来的"/login"
                    .loginPage("/user/login")
                    .permitAll()
                    .and().authorizeRequests().antMatchers("/health", "/css/**","/druid/**","/druid/index.html").permitAll()
                    // 测试资源
//                    .and().addFilterBefore(buildLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
//                    .addFilterBefore(buildTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
//                    .and().authorizeRequests().antMatchers("/test/**").access("#oauth2.hasScope('xx') or #oauth2.hasScope('read')")
//                    .and().exceptionHandling().authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)) // 这句在认证端不能写，会阻断登录页面跳转，暂时没解决
                    .and().authorizeRequests().anyRequest().authenticated()
                    .and().httpBasic()
            ;
        } else {
            System.out.println(" >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 未启用security " + isEnableSecurity);
            http.csrf().disable().authorizeRequests().anyRequest().permitAll();
        }


    }

    @Override
    public void init(final WebSecurity web) throws Exception {

        final HttpSecurity http = getHttp();
        web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
            public void run() {
                FilterSecurityInterceptor securityInterceptor = http
                        .getSharedObject(FilterSecurityInterceptor.class);
                web.securityInterceptor(securityInterceptor);
                securityInterceptor.setRunAsManager(new RunAsManagerImpl());
            }
        });
    }

}

/*
 * BCrypt Java实现
 * BCrypt也是一种哈希算法，相比MD5更安全，速度慢
 * */
class UseBCrypt {
    public static void main(String args[]) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请模拟用户输入明文密码：");
        String password = scanner.nextLine();//明文
        System.out.println("请输入待验证真实明文密码：");
        String candidate = scanner.nextLine();
        // 第一次哈希一个password
        String hashed = BCrypt.hashpw(password, BCrypt.gensalt());//密文

        // pig      $2a$10$AyvBsEwqa0x9WHNhREYxHO1JlYnpe6QRLYejKd6ZFb7KhZ2ibVwGe
        // pig      $2a$10$DI31iEDnHff1b7HhaQQUHeM489A7bFNQ/of0rJwPiw8HpD4zBvZYm

        // gensalt's log_rounds parameter determines the complexity
        // the work factor is 2**log_rounds, and the default is 10
//        String hashed2 = BCrypt.hashpw(password, BCrypt.gensalt(12));
        System.out.println("本次生成密文:"+hashed);
//        System.out.println(hashed2);
        //密码密文匹配检测
        if (BCrypt.checkpw(candidate, hashed))
            System.out.println("It matches");
        else
            System.out.println("It does not match");
    }
}
